﻿using LightCAD.Core.Elements;
using System.Text.Json.Serialization;

namespace LightCAD.Core
{
    [JsonDerivedType(typeof(LcModelSpace), typeDiscriminator: nameof(LcModelSpace))]
    [JsonDerivedType(typeof(LcBlock), typeDiscriminator: nameof(LcBlock))]
    public class ElementSet : LcObject, IElementSet
    {
        //程序集的IsFireChangedEvent主要控制向文档触发事件

        /// <summary>
        /// 所有元素集合，IElementContainer成员
        /// </summary>
        [JsonInclude]
        public ElementCollection Elements { get; internal set; } = new ElementCollection();

        
        public void InsertElement(LcElement element)
        {
            element.Parent = this;
            if (!IsFireChangedEvent)
            {
                element.Document = this.Document;
                this.Elements.Add(element);
                //如果元素集没有触发事件，根据元素本身触发事件，元素只触发After事件
                if (element.IsFireChangedEvent)
                {
                    var args = new ObjectChangedEventArgs
                    {
                         ElementSet=this,
                        Type = ObjectChangeType.Insert,
                        Target = element
                    };
                    element.OnObjectChangedAfter(args);
                }
            }
            else
            {
                var args = new ObjectChangedEventArgs
                {
                    ElementSet = this,
                    Type = ObjectChangeType.Insert,
                    Target = element
                };
                if (element.IsFireChangedEvent) 
                    element.OnObjectChangedBefore(args);
                if (args.IsCancel) return;

                this.OnObjectChangedBefore(args);
                if (args.IsCancel) return;

                this.Document.OnObjectChangedBefore(args);
                if (args.IsCancel) return;

                element.Document = this.Document;
                this.Elements.Add(element);


                if (element.IsFireChangedEvent)
                    element.OnObjectChangedAfter(args);

                this.OnObjectChangedAfter(args);
                this.Document.OnObjectChangedAfter(args);
            }
        }

        public bool RemoveElement(LcElement element)
        {
            if (element == null || this != element.Parent)
            {
                throw new ArgumentException(SR.ElementNotInContainer, nameof(element));
            }

            if (!IsFireChangedEvent)
            {
                var removed=this.Elements.Remove(element);
                element.Parent = null;
                //如果元素集没有触发事件，根据元素本身触发事件，元素只触发After事件
                if (element.IsFireChangedEvent)
                {
                    var args = new ObjectChangedEventArgs
                    {
                        ElementSet = this,
                        Type = ObjectChangeType.Remove,
                        Target = element
                    };
                    element.OnObjectChangedAfter(args);
                }
                return removed;
            }
            else
            {
                if (!this.Elements.Contains(element))
                {
                    return false;
                }

                var args = new ObjectChangedEventArgs
                {
                    ElementSet = this,
                    Type = ObjectChangeType.Remove,
                    Target = element
                };
                
                if(element.IsFireChangedEvent) 
                    element.OnObjectChangedBefore(args);
                if (args.IsCancel) return false;

                this.OnObjectChangedBefore(args);
                if (args.IsCancel) return false;

                this.Document.OnObjectChangedBefore(args);
                if (args.IsCancel) return false;

                this.Elements.Remove(element);
                element.Parent = null;

                if (element.IsFireChangedEvent) 
                    element.OnObjectChangedAfter(args);

                this.OnObjectChangedAfter(args);
                this.Document.OnObjectChangedAfter(args);
                return true;
            }
        }

        public LcClipAreaCollection ClipAreas { get; set; }

        public void MoveElement(LcElement element, double dx, double dy)
        {
            element.Translate(dx, dy);
        }

        public LcCircle AddCircle(Vector2d center, double radius)
        {
            var circle = this.Document.CreateObject<LcCircle>();
            circle.Center = center;
            circle.Radius = radius;
            this.InsertElement(circle);
            return circle;
        }

        public DimRadial AddDimRadial(Vector2d start, Vector2d end,string dimtext)
        {
            var dim = this.Document.CreateObject<DimRadial>();
            dim.Start = start;
            dim.End = end;
            dim.Dimtext = dimtext;
            this.InsertElement(dim);
            return dim;
        }
        public LcEllipse AddEllipse(Vector2d center,Vector2d axisX, double radiusX,double radiusY)
        {
            var ellipse = this.Document.CreateObject<LcEllipse>();
            ellipse.Center = center;
            ellipse.AxisX = axisX;
            ellipse.RadiusX = radiusX;
            ellipse.RadiusY = radiusY;
            this.InsertElement(ellipse);
            return ellipse;
        }

        public LcLine AddLine(Vector2d start, Vector2d end)
        {
            var line = this.Document.CreateObject<LcLine>();
            line.Start = start;
            line.End = end;
            this.InsertElement(line);
            return line;
        }

        public DimDiametric AddDimDiametric(Vector2d start, Vector2d end,string dimtext)
        {
            var dim= this.Document.CreateObject<DimDiametric>();
            dim.Start = start;
            dim.End = end;
            dim.Dimtext = dimtext;
            this.InsertElement(dim);
            return dim;
        }

        public LcRay AddRay(Vector2d start, Vector2d end)
        {
            var dir = (end - start).Normalized;
            var ray = this.Document.CreateObject<LcRay>();
            ray.StartPoint = start;
            ray.Direction = dir;
            this.InsertElement(ray);
            return ray;
        }

        public LcXLine AddXLine(Vector2d start, Vector2d end)
        {
            var dir = (end - start).Normalized;
            var xline = this.Document.CreateObject<LcXLine>();
            xline.StartPoint = start;
            xline.Direction = dir;
            this.InsertElement(xline);
            return xline;
        }

        public LcLine AddQLeader(Vector2d start, Vector2d second, Vector2d end)
        {
            var line = this.Document.CreateObject<LcLine>();
            line.Start = start;
            line.End = end;
            this.InsertElement(line);
            return line;
        }
    }
}